#include <android/asset_manager.h>
#include <android/asset_manager_jni.h>
#include <android/log.h>
#include <android/native_window.h>
#include <android/native_window_jni.h>  // ANativeWindow_fromSurface
#include <jni.h>

#include <memory>
#include <mutex>   // std::once_flag 的定义位置
#include <thread>  //

#include "include/native_codec_handle.h"
#include "jni_callback.h"
#include "jni_help.h"

#define JNI_TAG "JNI_CODE"
namespace codec {
static struct {
  jclass clazz = nullptr;
  jfieldID mPtr = nullptr;
} gNativeCodecImpl;
// 全局变量
AAssetManager *g_assetManager = nullptr;
CallbackInfo gCallbackInfo;  // c++层调用java层代码

std::string loadShaderFromAsset(const char *path) {
  if (!g_assetManager) {
    __android_log_print(ANDROID_LOG_ERROR, "ShaderLoader",
                        "AssetManager not initialized!");
    return "";
  }

  AAsset *asset = AAssetManager_open(g_assetManager, path, AASSET_MODE_BUFFER);
  if (!asset) {
    __android_log_print(ANDROID_LOG_ERROR, "ShaderLoader", "Failed to open: %s",
                        path);
    return "";
  }

  size_t length = AAsset_getLength(asset);
  char *buffer = new char[length + 1];
  AAsset_read(asset, buffer, length);
  buffer[length] = '\0';

  std::string result(buffer);
  AAsset_close(asset);
  delete[] buffer;

  return result;
}

static jlong nativeInit(JNIEnv *env, jclass /* clazz */) {
  return reinterpret_cast<jlong>(new NativeCodecHandler());
}

static NativeCodecHandler *getNativeCodecHandler(JNIEnv *env, jobject clazz) {
  return reinterpret_cast<NativeCodecHandler *>(
      env->GetLongField(clazz, gNativeCodecImpl.mPtr));
}
static void nativeRelease(JNIEnv *env, jobject nativeImplObj) {
  NativeCodecHandler *codec = getNativeCodecHandler(env, nativeImplObj);
  if (codec) {
    codec->release();
    delete codec;
    env->SetLongField(nativeImplObj, gNativeCodecImpl.mPtr,
                      0);  // 重置Java层指针
  }
  // 重置全局状态
  if (g_assetManager) {
    g_assetManager = nullptr;
  }
}
static void nativeStart(JNIEnv *env, jobject nativeImplObj) {
  NativeCodecHandler *codec = getNativeCodecHandler(env, nativeImplObj);
  codec->start();
}
static void nativeSetDataSource(JNIEnv *env, jobject nativeImplObj, jint fd) {
  NativeCodecHandler *codec = getNativeCodecHandler(env, nativeImplObj);
  codec->setDataSource(fd);
}

static void nativePrepare(JNIEnv *env, jobject nativeImplObj, jobject jsurface,
                          jobject assetManager) {
  NativeCodecHandler *codec = getNativeCodecHandler(env, nativeImplObj);
  ANativeWindow *rawWindow = ANativeWindow_fromSurface(env, jsurface);
  g_assetManager = AAssetManager_fromJava(env, assetManager);
  std::string vertexCode = loadShaderFromAsset("shaders/vertex.glsl");
  std::string fragmentCode = loadShaderFromAsset("shaders/fragment.glsl");
  codec->prepare(rawWindow, vertexCode, fragmentCode);
}

static void nativePause(JNIEnv *env, jobject nativeImplObj) {
  NativeCodecHandler *codec = getNativeCodecHandler(env, nativeImplObj);
  codec->pause();
}
static void nativeResume(JNIEnv *env, jobject nativeImplObj) {
  NativeCodecHandler *codec = getNativeCodecHandler(env, nativeImplObj);
  codec->resume();
}

static void nativeSeekTo(JNIEnv *env, jobject nativeImplObj, jfloat position) {
  NativeCodecHandler *codec = getNativeCodecHandler(env, nativeImplObj);
  codec->seekTo(position);
}

static void nativeChangeSurface(JNIEnv *env, jobject nativeImplObj, jint width,
                                jint height, jboolean isFullScreen) {
  NativeCodecHandler *codec = getNativeCodecHandler(env, nativeImplObj);
  codec->ChangeSurface(width, height, isFullScreen);
}

static void nativeSetCallback(JNIEnv *env, jobject nativeImplObj,
                              jobject callback) {
  NativeCodecHandler *codec = getNativeCodecHandler(env, nativeImplObj);
  codec->setCallback(callback);
}

static const JNINativeMethod gInputManagerMethods[] = {
    /* name, signature, funcPtr */
    {"init", "()J", (void *)nativeInit},
    {"setDataSource", "(I)V", (void *)nativeSetDataSource},
    {"prepare", "(Landroid/view/Surface;Landroid/content/res/AssetManager;)V",
     (void *)nativePrepare},
    {"start", "()V", (void *)nativeStart},
    {"pause", "()V", (void *)nativePause},
    {"resume", "()V", (void *)nativeResume},
    {"seekTo", "(F)V", (void *)nativeSeekTo},
    {"release", "()V", (void *)nativeRelease},
    {"changeSurface", "(IIZ)V", (void *)nativeChangeSurface},
    {"setCallback", "(Lcom/bzsg/simpleMediaPlayer/jni/NativeCodecCallback;)V",
     (void *)nativeSetCallback},

};  // java层调用c++层方法

int register_codec(JNIEnv *env) {
  __android_log_print(ANDROID_LOG_INFO, JNI_TAG, "register_codec");
  int methodCount =
      sizeof(gInputManagerMethods) / sizeof(gInputManagerMethods[0]);
  int res = jniRegisterNativeMethods(env,
                                     "com/bzsg/simpleMediaPlayer/jni/"
                                     "NativeCodecHandler$NativeImpl",
                                     gInputManagerMethods, methodCount);
  FIND_CLASS(gNativeCodecImpl.clazz,
             "com/bzsg/simpleMediaPlayer/jni/"
             "NativeCodecHandler$NativeImpl");
  gNativeCodecImpl.clazz = jclass(env->NewGlobalRef(gNativeCodecImpl.clazz));
  gNativeCodecImpl.mPtr = env->GetFieldID(gNativeCodecImpl.clazz, "ptr", "J");

  // // Callbacks
  jclass clazz;
  FIND_CLASS(clazz, "com/bzsg/simpleMediaPlayer/jni/NativeCodecCallback");
  gCallbackInfo.clazz = reinterpret_cast<jclass>(env->NewGlobalRef(clazz));
  GET_METHOD_ID(gCallbackInfo.onStateChanged, clazz, "onStateChanged",
                "(Lcom/bzsg/simpleMediaPlayer/data/PlayerStateCode;Landroid/os/"
                "Bundle;)V");

  return 0;
};
}  // namespace codec